package com.zeus.core.report;

import com.zeus.common.constants.ZeusConfigConstant;
import com.zeus.common.constants.ZeusConstant;
import com.zeus.common.enums.ZeusAlarmLevelEnum;
import com.zeus.common.enums.ZeusAlarmModelEnum;
import com.zeus.common.enums.ZeusEventTypeEnum;
import com.zeus.common.enums.ZeusSenderTypeEnum;
import com.zeus.common.log.ZeusLoggerConfig;
import com.zeus.common.utils.MarkdownUtils;
import com.zeus.common.utils.StringUtils;
import com.zeus.common.utils.TimeUtils;
import com.zeus.core.config.data.ZeusConfig;
import com.zeus.core.context.ZeusContext;
import com.zeus.core.factory.ZeusFactory;
import com.zeus.core.open.event.ZeusEvent;
import com.zeus.core.open.spi.ZeusMonitor;
import com.zeus.core.sender.ZeusDingTalkSender;
import com.zeus.core.sender.ZeusSender;
import com.zeus.core.task.GatherTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 宙斯报告助手抽象类
 *
 * @author f1ybear_wang  2022/2/22 7:57 下午
 */
public abstract class AbstractZeusReportClient extends AbstractBaseZeusReportClient {
    private final static Logger log = LoggerFactory.getLogger(AbstractZeusReportClient.class);

    /**
     * 是否添加到任务
     */
    protected boolean isAddTask;

    /**
     * 是否完成
     */
    protected boolean completed;

    protected boolean changed = true;

    /**
     * 事件内容
     */
    protected ZeusEvent zeusEvent;

    /**
     * 消息发送
     */
    protected List<ZeusSender> senderList;

    public AbstractZeusReportClient() {
        super();
        this.senderList = new ArrayList<>();
    }

    /**
     * 事件类型
     * 
     * @return ZeusEventTypeEnum
     * @author f1ybear_wang  2022/2/23 6:56 下午
     */
    public abstract ZeusEventTypeEnum eventType();

    @Override
    public boolean addCount() {
        if (this.completed) {
            return false;
        }

        // 使用规则添加
        this.gatherRule.addCount();
        changed = true;
        return true;
    }

    @Override
    public boolean hasChanged() {
        return changed;
    }

    @Override
    public void complete() {
        // 添加开关判断，增强控制
        if (!ZeusContext.getContext().alarmEnable()) {
            return;
        }

        // 如果完成，则结束
        if (this.completed) {
            return;
        }

        this.isAddTask = isAddTask || Objects.equals(this.model.getCode(), ZeusAlarmModelEnum.NORMAL.getCode());
        // 是否需要添加任务
        if (!this.isAddTask) {
            this.gatherRule.addTaskBefore();

            // 塞入任务列表
            GatherTask.addTask(this);
            this.isAddTask = true;
            this.gatherRule.addTaskAfter();
            return;
        }

        // 判断是否可以发送
        if (this.gatherRule.isCanSendReport()) {
            // 发送
            sendReport();
        }

        // 是否超时，超时则任务结束
        if (this.gatherRule.isTimeout()) {
            // 结束
            this.completed = true;
        }
    }

    @Override
    public boolean isCompleted() {
        if (!this.isAddTask) {
            return false;
        }
        // 发送失败次数过多，直接忽略
        if (this.gatherRule.getSendFailCount() > ZeusConstant.SEND_FAIL_COUNT_IGNORE) {
            return true;
        }

        // 发送完成
        return this.completed;
    }

    /**
     * 发送报告
     *
     * @author f1ybear_wang  2022/2/25 12:15 上午
     */
    protected void sendReport() {
        // 异步执行操作
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            // 1、解析扩展数据
            parsingExtend();

            // 2、加工钉钉报告
            if (isCanSendMessage(ZeusSenderTypeEnum.DING_TALK)) {
                workDingTalkReport();
            }

            // 3、发送消息
            if (isCanSendMessage() && changed) {
                sendMessage();
                changed = false;
            }

            // 4、发送SPI
            sendSpiReport();
        }, ZeusFactory.getExecutor());
        completableFuture.whenComplete((res, throwable) -> {
            // 执行结束
        });
    }

    /**
     * 1、解析扩展数据
     *
     * @author f1ybear_wang  2022/2/25 12:02 上午
     */
    protected abstract void parsingExtend();

    /**
     * 2、加工钉钉报告
     *
     * @author f1ybear_wang  2022/2/25 12:02 上午
     */
    private void workDingTalkReport() {
        // 1、加工消息内容
        String message = workDingTalkMessage();

        // 2、获取钉钉机器人
        List<ZeusConfig.DingTalkInfo> dingTalkList = getDingTalkList();

        // 3、加工发送者
        workDingTalkSender(dingTalkList, message);
    }

    /**
     * 3、发送消息
     *
     * @author f1ybear_wang  2022/2/25 12:03 上午
     */
    private void sendMessage() {
        if (this.senderList == null || this.senderList.isEmpty()) {
            if (log.isWarnEnabled() && ZeusLoggerConfig.needEnableLogger()) {
                log.warn("### 宙斯监控报警，发送消息失败，发送助手列表为空");
            }
            return;
        }

        boolean result = this.senderList.stream().map(ZeusSender::send).allMatch(e -> e.equals(true));
        if (!result) {
            super.sendMessageFail();
            this.gatherRule.sendMessageFail();
        } else {
            this.gatherRule.sendMessageSuccess();
        }

        // 清空
        this.senderList.clear();
    }

    /**
     * 4、发送SPI通知
     *
     * @author f1ybear_wang  2022/2/25 12:03 上午
     */
    private void sendSpiReport() {
        try {
            List<ZeusMonitor> monitorList = ZeusFactory.getMonitorList();
            if (monitorList == null || monitorList.isEmpty()) {
                return;
            }
            monitorList.forEach(e -> e.listener(zeusEvent));
        } catch (Throwable e) {
            if (log.isWarnEnabled() && ZeusLoggerConfig.needEnableLogger()) {
                log.warn("### 宙斯发送SPI通知出现异常 >> errMsg = {}", e.getMessage());
            }
        }
    }

    /**
     * 1、加工钉钉消息
     *
     * @return 消息
     * @author f1ybear_wang  2022/2/28 2:49 下午
     */
    protected abstract String workDingTalkMessage();

    /**
     * 2、获取钉钉机器人列表
     *
     * @return List<ZeusConfig.DingTalkInfo> 钉钉机器人列表
     * @author f1ybear_wang  2022/2/28 2:49 下午
     */
    protected List<ZeusConfig.DingTalkInfo> getDingTalkList() {
        ZeusAlarmLevelEnum levelEnum = Optional.ofNullable(this.gatherRule.alarmLevel()).orElse(ZeusAlarmLevelEnum.P4);
        List<ZeusConfig.DingTalkInfo> dingTalkList = ZeusContext.getContext().getDingTalkList(levelEnum.getCode());
        if (dingTalkList != null && !dingTalkList.isEmpty()) {
            return dingTalkList;
        }
        return ZeusContext.getContext().getBaseDingTalkList();
    }

    /**
     * 3、加工钉钉发送者
     *
     * @param dingTalkList 机器人列表
     * @param content 消息内容
     * @author f1ybear_wang  2022/2/28 2:56 下午
     */
    private void workDingTalkSender(List<ZeusConfig.DingTalkInfo> dingTalkList, String content) {
        if (dingTalkList == null || dingTalkList.isEmpty()) {
            return;
        }

        List<ZeusDingTalkSender> dingTalkSenders = dingTalkList.stream().map(item -> {
            ZeusDingTalkSender.Account account = new ZeusDingTalkSender.Account();
            account.setAccessToken(item.getAccessToken());
            account.setSecret(item.getSecret());

            ZeusDingTalkSender.MarkdownMessage message = new ZeusDingTalkSender.MarkdownMessage();
            message.setAtMobiles(item.getAtMobileList());
            message.setIsAtAll(item.getAtAll());
            message.setTitle(ZeusConfigConstant.DefaultValue.DING_TALK_MESSAGE_DEFAULT_TITLE);
            message.setText(content);
            return new ZeusDingTalkSender(account, message);
        }).collect(Collectors.toList());

        if (!dingTalkSenders.isEmpty()) {
            this.senderList.addAll(dingTalkSenders);
        }
    }

    /**
     * 获取内容标题
     * 
     * @return 标题
     * @author f1ybear_wang  2022/2/25 3:26 下午
     */
    protected String getTitle() {
        ZeusEventTypeEnum eventType = eventType();
        String start = "【宙斯-" + this.model.getName() + "】";

        // 获取报警级别
        ZeusAlarmLevelEnum level = Optional.ofNullable(this.gatherRule.alarmLevel()).orElse(ZeusAlarmLevelEnum.P4);

        String countString = "(持续" + TimeUtils.millisecondsToString(this.gatherRule.continueTime()) + ",共"
            + this.gatherRule.getCount() + "次)";
        if (ZeusAlarmLevelEnum.isHighLevel(level.getCode())) {
            return MarkdownUtils.getTitle(start + ZeusContext.getContext().getAppId() + "发生" + level.getCode() + "-"
                + eventType.getDescription() + "-" +countString, true);
        }

        String levelCode = ZeusAlarmLevelEnum.UN_KNOW.getCode().equals(level.getCode()) ? "" : level.getCode() + "-";
        return MarkdownUtils.getTitle(start + ZeusContext.getContext().getAppId() + "发生" + levelCode
            + eventType.getDescription() + "-" + countString, false);
    }

    /**
     * 获取ELK地址
     * 
     * @return ELK地址
     * @author f1ybear_wang  2022/2/25 12:15 上午
     */
    protected String getElkUrl() {
            String url = ZeusContext.getContext().getElkUrl();
            if(org.apache.commons.lang3.StringUtils.isAnyBlank(url,this.traceId)){
                return null;
            }
            return "[点我跳转哦]("+url.replaceAll(Pattern.quote("$traceId"), this.traceId)+")";
    }

    /**
     * traceId 是否为空
     *
     * @return boolean true:不为空,  false:为空
     * @author f1ybear_wang  2022/2/28 3:07 下午
     */
    protected boolean traceIdIsNotEmpty() {
        return traceId != null && !traceId.isEmpty() && !ZeusConstant.NA.equals(traceId)
            && !ZeusConstant.IGNORED_TRACE.equals(traceId);
    }

    protected String getRequestApp() {
        if (rpcData != null) {
            return rpcData.getRemoteApplicationName();
        }
        return null;
    }

    protected String getRequestIp() {
        if (rpcData != null) {
            String remoteAddress = rpcData.getRemoteAddress();
            if (remoteAddress != null && !remoteAddress.isEmpty()) {
                return remoteAddress;
            }
        }
        if (httpData != null) {
            return httpData.getIp();
        }
        return null;
    }

    protected String getRequestUrl() {
        if (httpData != null) {
            String requestUri = httpData.getRequestUri();
            if (requestUri != null && !requestUri.isEmpty()) {
                return "HTTP#" + httpData.getMethod() + "#" + requestUri;
            }
        }
        if (rpcData != null && rpcData.getPath() != null && !rpcData.getPath().isEmpty()) {
            return "DUBBO#" + rpcData.getPath() + "#" + rpcData.getMethodName();
        }
        return null;
    }

    protected String getMessage(String str) {
        return StringUtils.subMaxString(str, ZeusContext.getContext().getMessageLength());
    }

    protected String getExceptionMessage(String str) {
        return StringUtils.subMaxString(str, ZeusContext.getContext().getExceptionMessageLength());

    }

    protected String getStackTrace(String str) {
        return StringUtils.subMaxString(str, ZeusContext.getContext().getStackTraceLength());
    }

}
